let n = 25;
let m = 5;
font_size = 15;
let cx, cy; // center position
let m_counter = 1;
const list = new CircularLinkedList();
const removed = new LinkedList();

let arrow_elem = undefined;

function setup() {
    createCanvas(800, 800);
    radius = 1.5 * font_size * n / PI;
    cx = width / 2;
    cy = height / 2;

    for(let i = 1; i <= n; i++) {
        list.insert(i, list.size());
    }
    arrow_elem = list.getHead();
    step();
}

async function step() {
    while(list.getHead().next !== list.getHead()) {
        await sleep(1000);
        m_counter++;
        if(m_counter > m) {
            m_counter = 1;
            list.remove(arrow_elem.element);
            removed.insert(arrow_elem.element, removed.size());
        }
        arrow_elem = arrow_elem.next;
    }
}

function drawItem(index, removed) {
    let angle = radians((index - 1) * 360 / n);
    let x = cx + cos(angle) * radius;
    let y = cy + sin(angle) * radius;
    if(removed) {
        fill(169, 209, 142);
        stroke(112, 173, 71);
    } else {
        fill(255);
        stroke(112, 173, 71);
    }
    ellipse(x, y, font_size * 2, font_size * 2);
    stroke(0);
    fill(0);
    text(str(index), x, y);
}

function drawArrow(curr_node) {
    textAlign(CENTER, CENTER)
    textSize(16);
    text(str(m_counter), cx, cy + font_size)
    let angle = radians((curr_node.element - 1) * 360 / n);
    stroke(68, 114, 196);
    strokeWeight(4);
    line(cx, cy, cx + cos(angle) * radius, cy + sin(angle) * radius);
}

function draw() {
    background(230);
    // draw indicator
    drawArrow(arrow_elem);
    // draw description
    strokeWeight(1);
    textAlign(CENTER, CENTER)
    textSize(font_size);
    stroke(0);
    text('n='+str(n)+', m='+str(m), cx, cy + radius + 1.8 * font_size);
    // draw exist items
    curr = list.getHead();
    do {
        drawItem(curr.element, false);
        curr = curr.next;
    } while(curr !== list.head)
    // draw deleted items
    curr = removed.getHead();
    while(curr) {
        drawItem(curr.element, true);
        curr = curr.next;
    }
}

// custom helper function to deliberately slow down
// the sorting process and make visualization easy
function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}